-module(s1ap_ana).
-include("S1AP.hrl").
-include("protocol.hrl").
-export([do/3]).
%% refer to spec 26.413
-record(s1ap_context,{
       mme,
       enodeb,
       enodeb_pid
}).
do(asn1binary,{IPContext,{},Payload},{Index,DateTime}) ->
  do(asn1binary,{IPContext,#s1ap_context{},Payload},{Index,DateTime});
do(asn1binary,{IPContext,Sctp_Cxt,Payload},{Index,DateTime}) ->
%%	case asn1ct:decode(list_to_atom("S1AP"), 'S1AP-PDU',Payload) of %%with all-one solution
  case asn1ct:decode('S1AP-PDU-Descriptions','S1AP-PDU',Payload) of
	{ok,Value} ->
		{S1Ap_Nas,Msg_Summary} = do(Value),
    New_Sctp_Cxt = ne_status_update(IPContext,Sctp_Cxt,Msg_Summary),
		dispatch(S1Ap_Nas#s1ap_nas{index=Index,datetime = DateTime,connection=New_Sctp_Cxt});
	{error,Reason} ->
    io:format("~p~n",[Payload]),
		Sctp_Cxt
	end.

do({initiatingMessage,#'InitiatingMessage'{procedureCode= ?'id-uplinkNASTransport',value=#'UplinkNASTransport'{protocolIEs=Values}}}) ->
	Res = extract_msg(Values),
	{Res#s1ap_nas{s1ap_proc = ?'id-uplinkNASTransport'},enodeb_2_mme};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-downlinkNASTransport',value=#'DownlinkNASTransport'{protocolIEs=Values}}}) ->
	Res = extract_msg(Values),
	{Res#s1ap_nas{s1ap_proc = ?'id-downlinkNASTransport'},mme_2_enodeb};

%% sent by mme to request ebs
do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-InitialContextSetup',value=#'InitialContextSetupRequest'{protocolIEs=Values}}}) ->
	Res = extract_msg(Values),
	{Res#s1ap_nas{s1ap_proc = ?'id-InitialContextSetup'},mme_2_enodeb};

do({successfulOutcome,#'SuccessfulOutcome'{procedureCode=?'id-InitialContextSetup',value=#'InitialContextSetupResponse'{protocolIEs=Values}}}) ->
	Res = extract_msg(Values),
	{Res#s1ap_nas{s1ap_proc = ?'id-InitialContextSetup'},enodeb_2_mme};
%%
%% it is the start point for connection
do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-initialUEMessage',value=#'InitialUEMessage'{protocolIEs=Values}}}) ->
	Res = extract_msg(Values),
	{(Res#s1ap_nas{s1ap_proc = ?'id-initialUEMessage'}),enodeb_2_mme};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-ErrorIndication',value=#'ErrorIndication'{protocolIEs=Values}}}) ->
	Res = extract_msg(Values),
	{(Res#s1ap_nas{s1ap_proc = ?'id-ErrorIndication'}),undetermined};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-UEContextRelease',value=#'UEContextReleaseCommand'{protocolIEs=Values}}}) ->
	Res = extract_msg(Values),
	{Res#s1ap_nas{s1ap_proc = ?'id-UEContextRelease'},undetermined};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-UEContextReleaseRequest',value=#'UEContextReleaseRequest'{protocolIEs=Values}}}) ->
	Res = extract_msg(Values),
	{Res#s1ap_nas{s1ap_proc = ?'id-UEContextReleaseRequest'},undetermined};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-UECapabilityInfoIndication',value=#'UECapabilityInfoIndication'{protocolIEs=Values}}}) ->
	Res = extract_msg(Values),
	{(Res#s1ap_nas{s1ap_proc = ?'id-UECapabilityInfoIndication'}),enodeb_2_mme};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-Paging',value=#'Paging'{protocolIEs=Values}}}) ->
	%%Res = extract_msg(Values),
	{#s1ap_nas{s1ap_proc = ?'id-Paging'},mme_2_enodeb};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-HandoverPreparation',value=#'HandoverRequired'{protocolIEs=Values}}}) ->
	%%Res = extract_msg(Values),
	{#s1ap_nas{s1ap_proc = ?'id-HandoverPreparation'},enodeb_2_mme};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-HandoverResourceAllocation',value=#'HandoverRequest'{protocolIEs=Values}}}) ->
	%%Res = extract_msg(Values),
	{#s1ap_nas{s1ap_proc = ?'id-HandoverResourceAllocation'},mme_2_enodeb};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-S1Setup'}}) ->
	%%Res = extract_msg(Values),
	{#s1ap_nas{s1ap_proc = ?'id-S1Setup'},enodeb_2_mme};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-MMEStatusTransfer'}}) ->
	%%Res = extract_msg(Values),
	{#s1ap_nas{s1ap_proc = ?'id-MMEStatusTransfer'},mme_2_enodeb};

do({successfulOutcome,#'SuccessfulOutcome'{procedureCode=?'id-UEContextRelease',value=#'UEContextReleaseComplete'{protocolIEs=Values}}}) ->
	%%Res = extract_msg(Values),
	{#s1ap_nas{s1ap_proc = ?'id-UEContextRelease'},undetermined};

do({successfulOutcome,#'SuccessfulOutcome'{procedureCode=A}}) ->
  {#s1ap_nas{s1ap_proc = ignore},undetermined};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-eNBStatusTransfer'}}) ->
  {#s1ap_nas{s1ap_proc = ignore},undetermined};

do({initiatingMessage,#'InitiatingMessage'{procedureCode=?'id-eNBConfigurationTransfer'}}) ->
  {#s1ap_nas{s1ap_proc = ignore},undetermined};

do(Msg) ->
  io:format("unknown s1 message ~p ~n",[Msg]),
	{#s1ap_nas{s1ap_proc = unknown,nas = Msg},undetermined}.

extract_msg(Values) ->
	extract_msg(Values,#s1ap_nas{}).

extract_msg([#'ProtocolIE-Field'{id = ?'id-EUTRAN-CGI',
				value=#'EUTRAN-CGI'{pLMNidentity=PlmnId,'cell-ID' = CellId}}
			|Other],Res) ->
	extract_msg(Other,Res#s1ap_nas{cgi = {PlmnId,CellId}});

extract_msg([#'ProtocolIE-Field'{id = ?'id-TAI',
				value=#'TAI'{pLMNidentity=PlmnId,'tAC' = Tac}}
			|Other],Res) ->
	extract_msg(Other,Res#s1ap_nas{tai = {PlmnId,Tac}});

extract_msg([#'ProtocolIE-Field'{id = ?'id-MME-UE-S1AP-ID',
				value= MME_Id}
			|Other],Res) ->
	extract_msg(Other,Res#s1ap_nas{mme_ue_id = MME_Id});

extract_msg([#'ProtocolIE-Field'{id = ?'id-eNB-UE-S1AP-ID',
				value= ENB_Id}
			|Other],Res) ->
	extract_msg(Other,Res#s1ap_nas{enb_ue_id = ENB_Id});

extract_msg([#'ProtocolIE-Field'{id = ?'id-UE-S1AP-IDs',
				value = {_,Ids}}
%%				value = #'UE-S1AP-ID-pair'{'mME-UE-S1AP-ID' = MME_Id,'eNB-UE-S1AP-ID' = ENB_Id}}
			|Other],Res) ->
	extract_msg(Other,Res#s1ap_nas{enb_ue_id = Ids#'UE-S1AP-ID-pair'.'eNB-UE-S1AP-ID',mme_ue_id = Ids#'UE-S1AP-ID-pair'.'mME-UE-S1AP-ID'});

extract_msg([#'ProtocolIE-Field'{id = ?'id-NAS-PDU',
				value= NAS}
			|Other],Res) ->
	extract_msg(Other,Res#s1ap_nas{nas = extract_nas(NAS)});

extract_msg([#'ProtocolIE-Field'{id = ?'id-S-TMSI',value = #'S-TMSI'{'m-TMSI' = Tmsi,mMEC = MMEc}} | Other],Res) when is_list(Tmsi)->
	extract_msg(Other,Res#s1ap_nas{tmsi = {MMEc,binary:decode_unsigned(list_to_binary(Tmsi))}});

extract_msg([#'ProtocolIE-Field'{id = ?'id-E-RABToBeSetupListCtxtSUReq',value=[SURequest|Other_Request]}|Other],Res) when is_record(SURequest,'ProtocolIE-Field') ->
	#'E-RABToBeSetupItemCtxtSUReq'{'e-RAB-ID' = Rab_Id,
											transportLayerAddress=TAddr,'gTP-TEID' = TeId,'nAS-PDU' = Nas}
												= SURequest#'ProtocolIE-Field'.value,
	<<A1:8,A2:8,A3:8,A4:8>> = TAddr,
	<<TeId_Int:32>> = list_to_binary(TeId),
	Rab_Infos = [{Rab_Id,{A1,A2,A3,A4},TeId_Int}|Res#s1ap_nas.rab_info],
	extract_msg([#'ProtocolIE-Field'{id = ?'id-E-RABToBeSetupListCtxtSUReq',value=Other_Request}|Other],Res#s1ap_nas{rab_info=Rab_Infos,nas=extract_nas(Nas)});

extract_msg([#'ProtocolIE-Field'{id = ?'id-E-RABToBeSetupListCtxtSUReq',value=[]}|Other],Res) ->
	extract_msg(Other,Res);

extract_msg([#'ProtocolIE-Field'{id = ?'id-E-RABSetupListCtxtSURes',value = [SURes|Other_Res]}|Other],Res) when is_record(SURes,'ProtocolIE-Field') ->
	#'E-RABSetupItemCtxtSURes'{'e-RAB-ID' = Rab_Id,
											transportLayerAddress=TAddr,'gTP-TEID' = TeId}
												= SURes#'ProtocolIE-Field'.value,
	<<A1:8,A2:8,A3:8,A4:8>> = TAddr,
	<<TeId_Int:32>> = list_to_binary(TeId),
	Rab_Infos = [{Rab_Id,{A1,A2,A3,A4},TeId_Int}|Res#s1ap_nas.rab_info],
	extract_msg([#'ProtocolIE-Field'{id = ?'id-E-RABSetupListCtxtSURes',value = Other_Res}|Other],Res#s1ap_nas{rab_info=Rab_Infos});

extract_msg([#'ProtocolIE-Field'{id = ?'id-E-RABSetupListCtxtSURes',value=[]}|Other],Res) ->
	extract_msg(Other,Res);


extract_msg([#'ProtocolIE-Field'{id = ?'id-uEaggregateMaximumBitrate',value=BitRate}| Other],Res) ->
  {UpRate,DownRate} = {BitRate#'UEAggregateMaximumBitrate'.uEaggregateMaximumBitRateUL,
                      BitRate#'UEAggregateMaximumBitrate'.uEaggregateMaximumBitRateDL},
  extract_msg(Other,Res#s1ap_nas{rate = {UpRate,DownRate}});
extract_msg([#'ProtocolIE-Field'{id = ?'id-Cause',value = Value}|Other],Res) ->
  extract_msg(Other,Res#s1ap_nas{res = Value});

extract_msg([],Res) ->
	Res;

extract_msg([Cur|Other],Res) ->
	extract_msg(Other,Res).
extract_nas(Nas) when Nas =:= asn1_NOVALUE ->
	<<>>;
extract_nas(Nas) ->
	list_to_binary(Nas).

dispatch(S1Ap_Nas) ->
  case S1Ap_Nas#s1ap_nas.index of
  undefined ->
    io:format("message:~p~n",[S1Ap_Nas]);
  _ ->
    ok
  end,
  s1ap_dispatcher:do(S1Ap_Nas).

ne_status_update(#ip_protocol{source_ip=SIP,dest_ip=DIP},S1Ap_Cxt,mme_2_enodeb) ->
  {mme,enodeb} = {signalpoint_sup:msg_fresh(SIP,mme,send),signalpoint_sup:msg_fresh(DIP,enodeb,recv)},
  S1Ap_Cxt#s1ap_context{mme=SIP,enodeb=DIP};
ne_status_update(#ip_protocol{source_ip=SIP,dest_ip=DIP},S1Ap_Cxt,enodeb_2_mme) ->
  enodeb = signalpoint_sup:msg_fresh(SIP,enodeb,send),
  mme = signalpoint_sup:msg_fresh(DIP,mme,recv),
  S1Ap_Cxt#s1ap_context{mme=DIP,enodeb=SIP};
ne_status_update(#ip_protocol{source_ip=SIP,dest_ip=DIP},S1Ap_Cxt,undetermined) ->
  case {signalpoint_sup:msg_fresh(SIP,undetermined,send),signalpoint_sup:msg_fresh(DIP,undetermined,recv)} of
  {mme,enodeb} ->
    S1Ap_Cxt#s1ap_context{mme=SIP,enodeb=DIP};
  {enodeb,mme} ->
    S1Ap_Cxt#s1ap_context{mme=SIP,enodeb=DIP};
  _ ->
    S1Ap_Cxt
  end;
ne_status_update(IPContext,S1Ap_Cxt,Msg_Summary) ->
  S1Ap_Cxt.
